:art: Member Relative APIs

huangqimin 5 年之前
父节点
当前提交
4ce7f5c87b

+ 3 - 0
.gitignore

@@ -64,3 +64,6 @@ collect_static/
64 64
 
65 65
 # Special File
66 66
 *download.html
67
+
68
+# Qiniu
69
+.qiniu_pythonsdk_hostscache.json

+ 2 - 0
api/mch_views.py

@@ -398,6 +398,8 @@ def consumer_info_api(request):
398 398
         log.has_scan = True
399 399
         log.save()
400 400
 
401
+    # TODO: 发放会员权益
402
+
401 403
     return response(200, 'Submit Consumer Info Success', u'提交消费者信息成功')
402 404
 
403 405
 

+ 124 - 7
api/member_views.py

@@ -3,13 +3,13 @@
3 3
 from __future__ import division
4 4
 
5 5
 from django.conf import settings
6
+from django.db import transaction
6 7
 from django_logit import logit
7 8
 from django_response import response
8
-from paginator import pagination
9 9
 
10 10
 from account.models import UserInfo
11
-from member.models import GoodsInfo, MemberActivityInfo, RightInfo
12
-from utils.error.errno_utils import UserStatusCode
11
+from member.models import GoodsInfo, GoodsOrderInfo, MemberActivityInfo, MemberActivitySigninInfo, MemberActivitySignupInfo, RightInfo
12
+from utils.error.errno_utils import MemberActivityStatusCode, MemberGoodStatusCode, MemberRightStatusCode, UserStatusCode
13 13
 from utils.redis.rshot import get_shot_member_data
14 14
 
15 15
 
@@ -79,7 +79,7 @@ def right_detail(request):
79 79
     try:
80 80
         right = RightInfo.objects.get(right_id=right_id)
81 81
     except RightInfo.DoesNotExist:
82
-        return response()
82
+        return response(MemberRightStatusCode.RIGHT_NOT_FOUND)
83 83
 
84 84
     return response(200, data={
85 85
         'right': right.data,
@@ -133,7 +133,65 @@ def good_detail(request):
133 133
     try:
134 134
         good = GoodsInfo.objects.get(good_id=good_id)
135 135
     except GoodsInfo.DoesNotExist:
136
-        return response()
136
+        return response(MemberGoodStatusCode.GOOD_NOT_FOUND)
137
+
138
+    return response(200, data={
139
+        'nickname': user.final_nickname,
140
+        'avatar': user.avatar,
141
+        'integral': user.integral,
142
+        'freeze_integral': user.freeze_integral,
143
+        'final_integral': user.final_integral,
144
+        'shots_num': user.shots_num,
145
+        'level': user.level,
146
+        'good': good.data,
147
+    })
148
+
149
+
150
+@logit
151
+@transaction.atomic
152
+def good_exchange(request):
153
+    brand_id = request.POST.get('brand_id', settings.KODO_DEFAULT_BRAND_ID)
154
+    user_id = request.POST.get('user_id', '')
155
+    good_id = request.POST.get('good_id', '')
156
+    name = request.POST.get('name', '')
157
+    phone = request.POST.get('phone', '')
158
+    address = request.POST.get('address', '')
159
+
160
+    # 校验用户是否存在
161
+    try:
162
+        user = UserInfo.objects.select_for_update().get(user_id=user_id)
163
+    except UserInfo.DoesNotExist:
164
+        return response(UserStatusCode.USER_NOT_FOUND)
165
+
166
+    try:
167
+        good = GoodsInfo.objects.get(good_id=good_id)
168
+    except GoodsInfo.DoesNotExist:
169
+        return response(MemberGoodStatusCode.GOOD_NOT_FOUND)
170
+
171
+    if user.level < good.minlevel:
172
+        return response(MemberGoodStatusCode.GOOD_NO_EXCHANGE_PERMISSION)
173
+
174
+    if user.integral < good.integral:
175
+        return response(MemberGoodStatusCode.GOOD_INTEGRAL_NOT_ENOUGH)
176
+
177
+    user.integral -= good.integral
178
+    user.save()
179
+
180
+    GoodsOrderInfo.objects.create(
181
+        user_id=user_id,
182
+        good_id=good_id,
183
+        good_type=good.good_type,
184
+        name=name,
185
+        phone=phone,
186
+        address=address,
187
+    )
188
+
189
+    if good.good_type == GoodsInfo.PHYSICAL:
190
+        # TODO: 通知客服发快递
191
+        pass
192
+    else:
193
+        # TODO: 发放虚拟商品
194
+        pass
137 195
 
138 196
     return response(200, data={
139 197
         'nickname': user.final_nickname,
@@ -192,9 +250,68 @@ def activity_detail(request):
192 250
     activity_id = request.POST.get('activity_id')
193 251
 
194 252
     try:
195
-        act = MemberActivityInfo.objects.get(pk=activity_id, status=True)
253
+        act = MemberActivityInfo.objects.get(activity_id=activity_id, status=True)
196 254
     except MemberActivityInfo.DoesNotExist:
197
-        return response()
255
+        return response(MemberActivityStatusCode.ACTIVITY_NOT_FOUND)
256
+
257
+    return response(200, data={
258
+        'activity': act.data,
259
+    })
260
+
261
+
262
+@logit
263
+def activity_signup(request):
264
+    brand_id = request.POST.get('brand_id', settings.KODO_DEFAULT_BRAND_ID)
265
+    user_id = request.POST.get('user_id', '')
266
+    activity_id = request.POST.get('activity_id')
267
+    name = request.POST.get('name', '')
268
+    phone = request.POST.get('phone', '')
269
+
270
+    try:
271
+        act = MemberActivityInfo.objects.get(activity_id=activity_id, status=True)
272
+    except MemberActivityInfo.DoesNotExist:
273
+        return response(MemberActivityStatusCode.ACTIVITY_NOT_FOUND)
274
+
275
+    MemberActivitySignupInfo.objects.update_or_create(user_id=user_id, activity_id=activity_id, defaults={
276
+        'title': act.title,
277
+        'name': name,
278
+        'phone': phone,
279
+    })
280
+
281
+    # TODO: 立即推送模版消息(报名成功,时间,地点)
282
+    # TODO: 延迟(活动当天)推送模版消息(时间,地点)
283
+
284
+    return response(200, data={
285
+        'activity': act.data,
286
+    })
287
+
288
+
289
+@logit
290
+@transaction.atomic
291
+def activity_signin(request):
292
+    brand_id = request.POST.get('brand_id', settings.KODO_DEFAULT_BRAND_ID)
293
+    user_id = request.POST.get('user_id', '')
294
+    activity_id = request.POST.get('activity_id')
295
+
296
+    # 校验用户是否存在
297
+    try:
298
+        user = UserInfo.objects.select_for_update().get(user_id=user_id)
299
+    except UserInfo.DoesNotExist:
300
+        return response(UserStatusCode.USER_NOT_FOUND)
301
+
302
+    try:
303
+        act = MemberActivityInfo.objects.get(activity_id=activity_id, status=True)
304
+    except MemberActivityInfo.DoesNotExist:
305
+        return response(MemberActivityStatusCode.ACTIVITY_NOT_FOUND)
306
+
307
+    MemberActivitySigninInfo.objects.update_or_create(user_id=user_id, activity_id=activity_id, defaults={
308
+        'title': act.title,
309
+    })
310
+
311
+    user.integral += act.integral
312
+    user.save()
313
+
314
+    # TODO: 立即推送模版消息(感谢您参加活动,获得的积分)
198 315
 
199 316
     return response(200, data={
200 317
         'activity': act.data,

+ 5 - 0
api/urls.py

@@ -304,12 +304,17 @@ urlpatterns += [
304 304
 
305 305
     url(r'^member/rights$', member_views.rights, name='member_rights'),
306 306
     url(r'^member/right/detail$', member_views.right_detail, name='member_right_detail'),
307
+
307 308
     url(r'^member/goods$', member_views.goods, name='member_goods'),
308 309
     url(r'^member/good/detail$', member_views.good_detail, name='member_good_detail'),
310
+    url(r'^member/good/exchange$', member_views.good_exchange, name='member_good_exchange'),
311
+
309 312
     url(r'^member/integrals$', member_views.integrals, name='member_integrals'),
310 313
 
311 314
     url(r'^member/activity/list$', member_views.activity_list, name='member_activity_list'),
312 315
     url(r'^member/activity/detail$', member_views.activity_detail, name='member_activity_detail'),
316
+    url(r'^member/activity/signup$', member_views.activity_signup, name='member_activity_signup'),
317
+    url(r'^member/activity/signin$', member_views.activity_signin, name='member_activity_signin'),
313 318
 
314 319
     url(r'^rights$', member_views.rights, name='rights'),
315 320
     url(r'^right/detail$', member_views.right_detail, name='right_detail'),

+ 6 - 6
kodo/settings.py

@@ -246,12 +246,12 @@ QINIU = {
246 246
     'secret_key': '05sCekniLCgM6-d_PxrH8sFjvEOsx3ev-FgS7R-k',
247 247
     'bucket_default': 'photo',
248 248
     'buckets': {
249
-        'original': 'http://orf3sfb8s.bkt.clouddn.com',
250
-        'photo': 'http://orf3lnlmb.bkt.clouddn.com',
251
-        'prettify': 'http://orzfu8zxw.bkt.clouddn.com',
252
-        'thumbnail': 'http://orf3ahvt6.bkt.clouddn.com',
253
-        'thumbnail2': 'http://orf3muf5n.bkt.clouddn.com',
254
-        'watermark': 'http://orf3qne9f.bkt.clouddn.com',
249
+        'original': 'http://original.img.pai.ai',
250
+        'photo': 'http://photo.img.pai.ai',
251
+        'prettify': 'http://prettify.img.pai.ai',
252
+        'thumbnail': 'http://thumbnail.img.pai.ai',
253
+        'thumbnail2': 'http://thumbnail2.img.pai.ai',
254
+        'watermark': 'http://watermark.img.pai.ai',
255 255
     }
256 256
 }
257 257
 

+ 8 - 2
kodo/urls.py

@@ -99,7 +99,13 @@ urlpatterns += [
99 99
     url(r'^page/', include('page.urls', namespace='page')),
100 100
 ]
101 101
 
102
-urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
103
-urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
102
+# Simditor编辑器
103
+urlpatterns += [
104
+    url(r'^simditor/', include('simditor.urls'))
105
+]
106
+
107
+if settings.DEBUG:
108
+    urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
109
+    urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
104 110
 
105 111
 admin.site.site_header = u'[盈多]{}后台管理系统'.format(settings.KODO_BRAND_NAME)

+ 20 - 0
mch/migrations/0040_modelinfo_shot_member_name.py

@@ -0,0 +1,20 @@
1
+# -*- coding: utf-8 -*-
2
+# Generated by Django 1.11.26 on 2019-12-01 13:10
3
+from __future__ import unicode_literals
4
+
5
+from django.db import migrations, models
6
+
7
+
8
+class Migration(migrations.Migration):
9
+
10
+    dependencies = [
11
+        ('mch', '0039_auto_20191119_1348'),
12
+    ]
13
+
14
+    operations = [
15
+        migrations.AddField(
16
+            model_name='modelinfo',
17
+            name='shot_member_name',
18
+            field=models.CharField(blank=True, help_text='\u578b\u53f7\u5168\u540d\u79f0', max_length=255, null=True, verbose_name='shot_member_name'),
19
+        ),
20
+    ]

+ 36 - 2
member/admin.py

@@ -2,7 +2,7 @@
2 2
 
3 3
 from django.contrib import admin
4 4
 
5
-from member.models import GoodsInfo, MemberActivityInfo, ShotTypeInfo, RightInfo
5
+from member.models import GoodsInfo, GoodsOrderInfo, MemberActivityInfo, MemberActivitySigninInfo, MemberActivitySignupInfo, MemberCouponInfo, ShotTypeInfo, RightInfo
6 6
 from utils.redis.rshot import update_shot_member_data
7 7
 
8 8
 
@@ -11,6 +11,20 @@ class GoodsInfoAdmin(admin.ModelAdmin):
11 11
     list_filter = ('is_slider', 'status')
12 12
 
13 13
 
14
+class GoodsOrderInfoAdmin(admin.ModelAdmin):
15
+    list_display = ('order_id', 'user_id', 'good_id', 'good_type', 'title', 'name', 'phone', 'address', 'tracking_number', 'has_send_template_message', 'status', 'created_at', 'updated_at')
16
+
17
+    def save_model(self, request, obj, form, change):
18
+        obj.save()
19
+
20
+        if obj.has_send_template_message:
21
+            return
22
+
23
+        if obj.good_type == GoodsInfo.PHYSICAL:
24
+            # TODO: 立即推送模版消息,兑换成功,快递已发送(商品,快递单号,扣除的积分)
25
+            pass
26
+
27
+
14 28
 class RightInfoAdmin(admin.ModelAdmin):
15 29
     list_display = ('right_id', 'right_type', 'icon', 'title', 'subtitle', 'detail', 'level1', 'level2', 'level3', 'level4', 'level5',  'minlevel', 'position', 'status', 'created_at', 'updated_at')
16 30
     list_filter = ('right_type', 'status')
@@ -29,11 +43,31 @@ class ShotTypeInfoAdmin(admin.ModelAdmin):
29 43
 
30 44
 
31 45
 class MemberActivityInfoAdmin(admin.ModelAdmin):
32
-    list_display = ('title', 'subtitle', 'date', 'city', 'location', 'lat', 'lon', 'image', 'is_slider', 'slider_image', 'position', 'status', 'created_at', 'updated_at')
46
+    list_display = ('activity_id', 'title', 'subtitle', 'date', 'city', 'location', 'lat', 'lon', 'image', 'is_slider', 'slider_image', 'position', 'status', 'created_at', 'updated_at')
33 47
     list_filter = ('is_slider', 'status')
34 48
 
35 49
 
50
+class MemberActivitySignupInfoAdmin(admin.ModelAdmin):
51
+    list_display = ('signup_id', 'activity_id', 'title', 'name', 'phone', 'status', 'created_at', 'updated_at')
52
+
53
+
54
+class MemberActivitySignupInfoAdmin(admin.ModelAdmin):
55
+    list_display = ('signup_id', 'user_id', 'activity_id', 'title', 'name', 'phone', 'status', 'created_at', 'updated_at')
56
+
57
+
58
+class MemberActivitySigninInfoAdmin(admin.ModelAdmin):
59
+    list_display = ('signin_id', 'user_id', 'activity_id', 'title', 'status', 'created_at', 'updated_at')
60
+
61
+
62
+class MemberCouponInfoAdmin(admin.ModelAdmin):
63
+    list_display = ('coupon_id', 'user_id', 'coupon_type', 'coupon_start_at', 'coupon_expire_at', 'coupon_value', 'has_used', 'admin_id', 'used_at', 'status', 'created_at', 'updated_at')
64
+
65
+
36 66
 admin.site.register(GoodsInfo, GoodsInfoAdmin)
67
+admin.site.register(GoodsOrderInfo, GoodsOrderInfoAdmin)
37 68
 admin.site.register(RightInfo, RightInfoAdmin)
38 69
 admin.site.register(ShotTypeInfo, ShotTypeInfoAdmin)
39 70
 admin.site.register(MemberActivityInfo, MemberActivityInfoAdmin)
71
+admin.site.register(MemberActivitySignupInfo, MemberActivitySignupInfoAdmin)
72
+admin.site.register(MemberActivitySigninInfo, MemberActivitySigninInfoAdmin)
73
+admin.site.register(MemberCouponInfo, MemberCouponInfoAdmin)

+ 110 - 0
member/migrations/0006_auto_20191201_2110.py

@@ -0,0 +1,110 @@
1
+# -*- coding: utf-8 -*-
2
+# Generated by Django 1.11.26 on 2019-12-01 13:10
3
+from __future__ import unicode_literals
4
+
5
+from django.db import migrations, models
6
+import shortuuidfield.fields
7
+
8
+
9
+class Migration(migrations.Migration):
10
+
11
+    dependencies = [
12
+        ('member', '0005_memberactivityinfo_city'),
13
+    ]
14
+
15
+    operations = [
16
+        migrations.CreateModel(
17
+            name='GoodsOrderInfo',
18
+            fields=[
19
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
20
+                ('status', models.BooleanField(db_index=True, default=True, help_text='Status', verbose_name='status')),
21
+                ('created_at', models.DateTimeField(auto_now_add=True, help_text='Create Time', verbose_name='created_at')),
22
+                ('updated_at', models.DateTimeField(auto_now=True, help_text='Update Time', verbose_name='updated_at')),
23
+                ('order_id', shortuuidfield.fields.ShortUUIDField(blank=True, db_index=True, editable=False, help_text='\u8ba2\u5355\u552f\u4e00\u6807\u8bc6', max_length=22, null=True, unique=True)),
24
+                ('user_id', models.CharField(blank=True, db_index=True, help_text='\u7528\u6237\u552f\u4e00\u6807\u8bc6', max_length=32, null=True, verbose_name='user_id')),
25
+                ('good_id', models.CharField(blank=True, db_index=True, help_text='\u5546\u54c1\u552f\u4e00\u6807\u8bc6', max_length=32, null=True, verbose_name='good_id')),
26
+                ('good_type', models.IntegerField(choices=[(0, '\u5b9e\u7269'), (1, '\u865a\u62df')], db_index=True, default=1, help_text='\u5546\u54c1\u7c7b\u578b', verbose_name='good_type')),
27
+                ('title', models.CharField(blank=True, help_text='\u5546\u54c1\u540d\u79f0', max_length=255, null=True, verbose_name='title')),
28
+                ('name', models.CharField(blank=True, help_text='\u59d3\u540d', max_length=255, null=True, verbose_name='name')),
29
+                ('phone', models.CharField(blank=True, help_text='\u7535\u8bdd', max_length=255, null=True, verbose_name='phone')),
30
+                ('address', models.CharField(blank=True, help_text='\u5730\u5740', max_length=255, null=True, verbose_name='address')),
31
+                ('tracking_number', models.CharField(blank=True, help_text='\u5feb\u9012\u5355\u53f7', max_length=255, null=True, verbose_name='tracking_number')),
32
+                ('has_send_template_message', models.BooleanField(db_index=True, default=True, help_text='\u662f\u5426\u5df2\u53d1\u9001\u6a21\u7248\u6d88\u606f', verbose_name='has_send_template_message')),
33
+            ],
34
+            options={
35
+                'verbose_name': '\u4f1a\u5458\u5546\u54c1\u8ba2\u5355\u4fe1\u606f',
36
+                'verbose_name_plural': '\u4f1a\u5458\u5546\u54c1\u8ba2\u5355\u4fe1\u606f',
37
+            },
38
+        ),
39
+        migrations.CreateModel(
40
+            name='MemberActivitySigninInfo',
41
+            fields=[
42
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
43
+                ('status', models.BooleanField(db_index=True, default=True, help_text='Status', verbose_name='status')),
44
+                ('created_at', models.DateTimeField(auto_now_add=True, help_text='Create Time', verbose_name='created_at')),
45
+                ('updated_at', models.DateTimeField(auto_now=True, help_text='Update Time', verbose_name='updated_at')),
46
+                ('signin_id', shortuuidfield.fields.ShortUUIDField(blank=True, db_index=True, editable=False, help_text='\u6d3b\u52a8\u7b7e\u5230\u552f\u4e00\u6807\u8bc6', max_length=22, null=True, unique=True)),
47
+                ('user_id', models.CharField(blank=True, db_index=True, help_text='\u7528\u6237\u552f\u4e00\u6807\u8bc6', max_length=32, null=True, verbose_name='user_id')),
48
+                ('activity_id', models.CharField(blank=True, db_index=True, help_text='\u6d3b\u52a8\u552f\u4e00\u6807\u8bc6', max_length=32, null=True, verbose_name='activity_id')),
49
+                ('title', models.CharField(blank=True, help_text='\u6d3b\u52a8\u540d\u79f0', max_length=255, null=True, verbose_name='title')),
50
+            ],
51
+            options={
52
+                'verbose_name': '\u4f1a\u5458\u6d3b\u52a8\u7b7e\u5230\u4fe1\u606f',
53
+                'verbose_name_plural': '\u4f1a\u5458\u6d3b\u52a8\u7b7e\u5230\u4fe1\u606f',
54
+            },
55
+        ),
56
+        migrations.CreateModel(
57
+            name='MemberActivitySignupInfo',
58
+            fields=[
59
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
60
+                ('status', models.BooleanField(db_index=True, default=True, help_text='Status', verbose_name='status')),
61
+                ('created_at', models.DateTimeField(auto_now_add=True, help_text='Create Time', verbose_name='created_at')),
62
+                ('updated_at', models.DateTimeField(auto_now=True, help_text='Update Time', verbose_name='updated_at')),
63
+                ('signup_id', shortuuidfield.fields.ShortUUIDField(blank=True, db_index=True, editable=False, help_text='\u6d3b\u52a8\u62a5\u540d\u552f\u4e00\u6807\u8bc6', max_length=22, null=True, unique=True)),
64
+                ('user_id', models.CharField(blank=True, db_index=True, help_text='\u7528\u6237\u552f\u4e00\u6807\u8bc6', max_length=32, null=True, verbose_name='user_id')),
65
+                ('activity_id', models.CharField(blank=True, db_index=True, help_text='\u6d3b\u52a8\u552f\u4e00\u6807\u8bc6', max_length=32, null=True, verbose_name='activity_id')),
66
+                ('title', models.CharField(blank=True, help_text='\u6d3b\u52a8\u540d\u79f0', max_length=255, null=True, verbose_name='title')),
67
+                ('name', models.CharField(blank=True, help_text='\u59d3\u540d', max_length=255, null=True, verbose_name='name')),
68
+                ('phone', models.CharField(blank=True, help_text='\u7535\u8bdd', max_length=255, null=True, verbose_name='phone')),
69
+            ],
70
+            options={
71
+                'verbose_name': '\u4f1a\u5458\u6d3b\u52a8\u62a5\u540d\u4fe1\u606f',
72
+                'verbose_name_plural': '\u4f1a\u5458\u6d3b\u52a8\u62a5\u540d\u4fe1\u606f',
73
+            },
74
+        ),
75
+        migrations.CreateModel(
76
+            name='MemberCouponInfo',
77
+            fields=[
78
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
79
+                ('status', models.BooleanField(db_index=True, default=True, help_text='Status', verbose_name='status')),
80
+                ('created_at', models.DateTimeField(auto_now_add=True, help_text='Create Time', verbose_name='created_at')),
81
+                ('updated_at', models.DateTimeField(auto_now=True, help_text='Update Time', verbose_name='updated_at')),
82
+                ('coupon_id', shortuuidfield.fields.ShortUUIDField(blank=True, db_index=True, editable=False, help_text='\u5238\u552f\u4e00\u6807\u8bc6', max_length=22, null=True, unique=True)),
83
+                ('user_id', models.CharField(blank=True, db_index=True, help_text='\u7528\u6237\u552f\u4e00\u6807\u8bc6', max_length=32, null=True, verbose_name='user_id')),
84
+                ('coupon_type', models.IntegerField(choices=[(0, '\u6df1\u5ea6\u6e05\u6d01'), (1, '\u514d\u8d39\u8c03\u7126'), (2, '\u5916\u89c2\u6e05\u6d01'), (3, '\u610f\u5916\u7ef4\u4fee'), (4, '\u7ef4\u4fee\u4eba\u5de5')], db_index=True, default=0, help_text='\u5238\u7c7b\u578b', verbose_name='coupon_type')),
85
+                ('coupon_start_at', models.DateTimeField(blank=True, help_text='\u5238\u751f\u6548\u65f6\u95f4', null=True, verbose_name='coupon_start_at')),
86
+                ('coupon_expire_at', models.DateTimeField(blank=True, help_text='\u5238\u8fc7\u671f\u65f6\u95f4', null=True, verbose_name='coupon_expire_at')),
87
+                ('coupon_value', models.IntegerField(default=0, help_text='\u5238\u91d1\u989d\uff08\u5355\u4f4d\uff1a\u5206\uff09', verbose_name='coupon_value')),
88
+                ('has_used', models.BooleanField(db_index=True, default=False, help_text='\u662f\u5426\u5df2\u6838\u9500', verbose_name='has_used')),
89
+                ('admin_id', models.CharField(blank=True, db_index=True, help_text='\u6838\u9500\u5458\u552f\u4e00\u6807\u8bc6', max_length=32, null=True, verbose_name='admin_id')),
90
+                ('used_at', models.DateTimeField(blank=True, help_text='\u7ef4\u4fee\u5238\u6838\u9500\u65f6\u95f4', null=True, verbose_name='used_at')),
91
+            ],
92
+            options={
93
+                'verbose_name': '\u4f1a\u5458\u6d3b\u52a8\u7b7e\u5230\u4fe1\u606f',
94
+                'verbose_name_plural': '\u4f1a\u5458\u6d3b\u52a8\u7b7e\u5230\u4fe1\u606f',
95
+            },
96
+        ),
97
+        migrations.AddField(
98
+            model_name='memberactivityinfo',
99
+            name='activity_id',
100
+            field=shortuuidfield.fields.ShortUUIDField(blank=True, db_index=True, editable=False, help_text='\u6d3b\u52a8\u552f\u4e00\u6807\u8bc6', max_length=22, null=True, unique=True),
101
+        ),
102
+        migrations.AlterUniqueTogether(
103
+            name='memberactivitysignupinfo',
104
+            unique_together=set([('user_id', 'activity_id')]),
105
+        ),
106
+        migrations.AlterUniqueTogether(
107
+            name='memberactivitysignininfo',
108
+            unique_together=set([('user_id', 'activity_id')]),
109
+        ),
110
+    ]

+ 134 - 3
member/models.py

@@ -27,7 +27,7 @@ class GoodsInfo(BaseModelMixin):
27 27
 
28 28
     image = models.ImageField(_(u'image'), upload_to=upload_path, blank=True, null=True, help_text=u'商品图片')
29 29
 
30
-    is_slider = models.BooleanField(_(u'is_slider'), default=True, help_text=_(u'是否为轮播商品'), db_index=True)
30
+    is_slider = models.BooleanField(_(u'is_slider'), default=True, help_text=u'是否为轮播商品', db_index=True)
31 31
     slider_image = models.ImageField(_(u'slider_image'), upload_to=upload_path, blank=True, null=True, help_text=u'商品轮播图片')
32 32
 
33 33
     integral = models.IntegerField(_(u'integral'), default=99999, help_text=u'兑换所需积分')
@@ -76,6 +76,40 @@ class GoodsInfo(BaseModelMixin):
76 76
         }
77 77
 
78 78
 
79
+class GoodsOrderInfo(BaseModelMixin):
80
+    PHYSICAL = 0
81
+    VIRTUAL = 1
82
+
83
+    GOOD_TYPE_TUPLE = (
84
+        (PHYSICAL, u'实物'),
85
+        (VIRTUAL, u'虚拟'),
86
+    )
87
+
88
+    order_id = ShortUUIDField(_(u'order_id'), max_length=32, blank=True, null=True, help_text=u'订单唯一标识', db_index=True, unique=True)
89
+
90
+    user_id = models.CharField(_(u'user_id'), max_length=32, blank=True, null=True, help_text=u'用户唯一标识', db_index=True)
91
+
92
+    good_id = models.CharField(_(u'good_id'), max_length=32, blank=True, null=True, help_text=u'商品唯一标识', db_index=True)
93
+    good_type = models.IntegerField(_(u'good_type'), choices=GOOD_TYPE_TUPLE, default=VIRTUAL, help_text=u'商品类型', db_index=True)
94
+
95
+    title = models.CharField(_(u'title'), max_length=255, blank=True, null=True, help_text=u'商品名称')
96
+
97
+    name = models.CharField(_(u'name'), max_length=255, blank=True, null=True, help_text=u'姓名')
98
+    phone = models.CharField(_(u'phone'), max_length=255, blank=True, null=True, help_text=u'电话')
99
+    address = models.CharField(_(u'address'), max_length=255, blank=True, null=True, help_text=u'地址')
100
+
101
+    tracking_number = models.CharField(_(u'tracking_number'), max_length=255, blank=True, null=True, help_text=u'快递单号')
102
+
103
+    has_send_template_message = models.BooleanField(_(u'has_send_template_message'), default=True, help_text=u'是否已发送模版消息', db_index=True)
104
+
105
+    class Meta:
106
+        verbose_name = _(u'会员商品订单信息')
107
+        verbose_name_plural = _(u'会员商品订单信息')
108
+
109
+    def __unicode__(self):
110
+        return unicode(self.pk)
111
+
112
+
79 113
 class RightInfo(BaseModelMixin):
80 114
     PHYSICAL = 0
81 115
     VIRTUAL = 1
@@ -169,6 +203,8 @@ class ShotTypeInfo(BaseModelMixin):
169 203
 
170 204
 
171 205
 class MemberActivityInfo(BaseModelMixin):
206
+    activity_id = ShortUUIDField(_(u'activity_id'), max_length=32, blank=True, null=True, help_text=u'活动唯一标识', db_index=True, unique=True)
207
+
172 208
     title = models.CharField(_(u'title'), max_length=255, blank=True, null=True, help_text=u'活动名称')
173 209
     subtitle = models.CharField(_(u'subtitle'), max_length=255, blank=True, null=True, help_text=u'活动二级名称')
174 210
 
@@ -184,7 +220,7 @@ class MemberActivityInfo(BaseModelMixin):
184 220
 
185 221
     cover = models.ImageField(_(u'cover'), upload_to=upload_path, blank=True, null=True, help_text=u'活动列表图片')
186 222
 
187
-    is_slider = models.BooleanField(_(u'is_slider'), default=True, help_text=_(u'是否为轮播活动'), db_index=True)
223
+    is_slider = models.BooleanField(_(u'is_slider'), default=True, help_text=u'是否为轮播活动', db_index=True)
188 224
     slider_image = models.ImageField(_(u'slider_image'), upload_to=upload_path, blank=True, null=True, help_text=u'活动轮播图片')
189 225
 
190 226
     content_rich_text = RichTextField(_(u'content_rich_text'), blank=True, null=True, help_text=u'活动描述')
@@ -228,7 +264,8 @@ class MemberActivityInfo(BaseModelMixin):
228 264
     @property
229 265
     def data(self):
230 266
         return {
231
-            'id': self.pk,
267
+            'id': self.activity_id,
268
+            'activity_id': self.activity_id,
232 269
             'title': self.title,
233 270
             'subtitle': self.subtitle,
234 271
             'date': tc.local_string(self.date, '%Y-%m-%d'),
@@ -245,3 +282,97 @@ class MemberActivityInfo(BaseModelMixin):
245 282
             'state': 0,
246 283
             'is_signed': 0,
247 284
         }
285
+
286
+
287
+class MemberActivitySignupInfo(BaseModelMixin):
288
+    signup_id = ShortUUIDField(_(u'signup_id'), max_length=32, blank=True, null=True, help_text=u'活动报名唯一标识', db_index=True, unique=True)
289
+
290
+    user_id = models.CharField(_(u'user_id'), max_length=32, blank=True, null=True, help_text=u'用户唯一标识', db_index=True)
291
+
292
+    activity_id = models.CharField(_(u'activity_id'), max_length=32, blank=True, null=True, help_text=u'活动唯一标识', db_index=True)
293
+
294
+    title = models.CharField(_(u'title'), max_length=255, blank=True, null=True, help_text=u'活动名称')
295
+
296
+    name = models.CharField(_(u'name'), max_length=255, blank=True, null=True, help_text=u'姓名')
297
+    phone = models.CharField(_(u'phone'), max_length=255, blank=True, null=True, help_text=u'电话')
298
+
299
+    class Meta:
300
+        verbose_name = _(u'会员活动报名信息')
301
+        verbose_name_plural = _(u'会员活动报名信息')
302
+
303
+        unique_together = (
304
+            ('user_id', 'activity_id'),
305
+        )
306
+
307
+    def __unicode__(self):
308
+        return unicode(self.pk)
309
+
310
+
311
+class MemberActivitySigninInfo(BaseModelMixin):
312
+    signin_id = ShortUUIDField(_(u'signin_id'), max_length=32, blank=True, null=True, help_text=u'活动签到唯一标识', db_index=True, unique=True)
313
+
314
+    user_id = models.CharField(_(u'user_id'), max_length=32, blank=True, null=True, help_text=u'用户唯一标识', db_index=True)
315
+
316
+    activity_id = models.CharField(_(u'activity_id'), max_length=32, blank=True, null=True, help_text=u'活动唯一标识', db_index=True)
317
+
318
+    title = models.CharField(_(u'title'), max_length=255, blank=True, null=True, help_text=u'活动名称')
319
+
320
+    class Meta:
321
+        verbose_name = _(u'会员活动签到信息')
322
+        verbose_name_plural = _(u'会员活动签到信息')
323
+
324
+        unique_together = (
325
+            ('user_id', 'activity_id'),
326
+        )
327
+
328
+    def __unicode__(self):
329
+        return unicode(self.pk)
330
+
331
+
332
+class MemberCouponInfo(BaseModelMixin):
333
+    DEEP_CLEANING = 0
334
+    FREE_FOCUS = 1
335
+    CLEAN_APPEARANCE = 2
336
+    ACCIDENTAL_MAINTENANCE = 3
337
+    MAINTENANCE_MANPOWER = 4
338
+
339
+    COUPON_TYPE_TUPLE = (
340
+        (DEEP_CLEANING, u'深度清洁'),
341
+        (FREE_FOCUS, u'免费调焦'),
342
+        (CLEAN_APPEARANCE, u'外观清洁'),
343
+        (ACCIDENTAL_MAINTENANCE, u'意外维修'),
344
+        (MAINTENANCE_MANPOWER, u'维修人工')
345
+    )
346
+
347
+    coupon_id = ShortUUIDField(_(u'coupon_id'), max_length=32, blank=True, null=True, help_text=u'券唯一标识', db_index=True, unique=True)
348
+
349
+    user_id = models.CharField(_(u'user_id'), max_length=32, blank=True, null=True, help_text=u'用户唯一标识', db_index=True)
350
+
351
+    coupon_type = models.IntegerField(_(u'coupon_type'), choices=COUPON_TYPE_TUPLE, default=DEEP_CLEANING, help_text=u'券类型', db_index=True)
352
+    coupon_start_at = models.DateTimeField(_(u'coupon_start_at'), blank=True, null=True, help_text=u'券生效时间')
353
+    coupon_expire_at = models.DateTimeField(_(u'coupon_expire_at'), blank=True, null=True, help_text=u'券过期时间')
354
+    coupon_value = models.IntegerField(_(u'coupon_value'), default=0, help_text=u'券金额(单位:分)')
355
+
356
+    has_used = models.BooleanField(_(u'has_used'), default=False, help_text=u'是否已核销', db_index=True)
357
+    admin_id = models.CharField(_(u'admin_id'), max_length=32, blank=True, null=True, help_text=u'核销员唯一标识', db_index=True)
358
+    used_at = models.DateTimeField(_(u'used_at'), blank=True, null=True, help_text=u'维修券核销时间')
359
+
360
+    class Meta:
361
+        verbose_name = _(u'会员券信息')
362
+        verbose_name_plural = _(u'会员券信息')
363
+
364
+    def __unicode__(self):
365
+        return unicode(self.pk)
366
+
367
+    @property
368
+    def data(self):
369
+        return {
370
+            'coupon_id': self.coupon_id,
371
+            'coupon_type': self.coupon_type,
372
+            'coupon_start_at': tc.local_string(self.coupon_start_at, format='%Y%m%d'),
373
+            'coupon_expire_at': tc.local_string(self.coupon_expire_at, format='%Y%m%d'),
374
+            'coupon_value': self.coupon_value,
375
+            'has_used': self.has_used,
376
+            'admin_id': self.admin_id,
377
+            'used_at': tc.local_string(self.used_at, format='%Y%m%d'),
378
+        }

+ 1 - 1
simditor/views.py

@@ -59,7 +59,7 @@ def upload_handler(request):
59 59
                        'msg': '图片格式错误!'}
60 60
             return JsonResponse(retdata)
61 61
 
62
-    key = upload(uploaded_file.read()).get('key', '')
62
+    key = upload(uploaded_file.read())
63 63
     if not key:
64 64
         retdata = {'file_path': key, 'success': False, 'msg': '上传失败,请重试!'}
65 65
     else:

+ 18 - 0
utils/error/errno_utils.py

@@ -62,6 +62,24 @@ class ProductStatusCode(BaseStatusCode):
62 62
     PRODUCT_NOT_USED = StatusCodeField(502012, 'Product Not Used', description=u'产品未使用')
63 63
 
64 64
 
65
+class MemberGoodStatusCode(BaseStatusCode):
66
+    """ 会员商品相关错误码 5035xx """
67
+    GOOD_NOT_FOUND = StatusCodeField(503501, 'Good Not Found', description=u'商品不存在')
68
+
69
+    GOOD_NO_EXCHANGE_PERMISSION = StatusCodeField(503502, 'Good No Exchange Permission', description=u'商品无兑换权限')
70
+    GOOD_INTEGRAL_NOT_ENOUGH = StatusCodeField(503503, 'Good Integral Not Enough', description=u'商品兑换积分不足')
71
+
72
+
73
+class MemberRightStatusCode(BaseStatusCode):
74
+    """ 会员商品相关错误码 5036xx """
75
+    RIGHT_NOT_FOUND = StatusCodeField(503601, 'Right Not Found', description=u'权益不存在')
76
+
77
+
78
+class MemberActivityStatusCode(BaseStatusCode):
79
+    """ 会员活动相关错误码 5037xx """
80
+    ACTIVITY_NOT_FOUND = StatusCodeField(503701, 'Activity Not Found', description=u'活动不存在')
81
+
82
+
65 83
 class LensmanStatusCode(BaseStatusCode):
66 84
     """ 摄影师相关错误码 4000xx """
67 85
     LENSMAN_NOT_FOUND = StatusCodeField(400001, 'Lensman Not Found', description=u'摄影师不存在')

+ 1 - 1
utils/qiniucdn.py

@@ -39,7 +39,7 @@ def upload_file_path(path, key=None, mime_type='application/octet-stream', bucke
39 39
     return ret.get('key')
40 40
 
41 41
 
42
-def qiniu_file_url(key, bucket):
42
+def qiniu_file_url(key, bucket=QINIU['bucket_default']):
43 43
     if not key:
44 44
         return ''
45 45
     return '{}/{}'.format(QINIU['buckets'][bucket], key)